package com.gph.saviorframework.wf.service.impl;

import com.gph.saviorframework.Constants;
import com.gph.saviorframework.wf.service.ActivitiDefService;

import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.impl.ProcessEngineImpl;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmActivity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.delegate.ActivityBehavior;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.task.TaskDefinition;
import org.activiti.engine.repository.*;
import org.activiti.image.ProcessDiagramGenerator;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.*;
import java.util.zip.ZipInputStream;

public class ActivitiDefServiceImpl implements ActivitiDefService {

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private ProcessEngine processEngine;

    /**
     * 根据流程定义编号获取流程定义实体
     *
     * @param processDefinitionId 流程定义编号
     * @return 流程定义实体
     */
    @Transactional(readOnly = true)
    public ProcessDefinitionEntity getProcessDefinitionEntity(String processDefinitionId) {
        ProcessDefinitionEntity def =
                (ProcessDefinitionEntity) ((RepositoryServiceImpl)repositoryService)
                        .getDeployedProcessDefinition(processDefinitionId);
        return def;
    }

    /**
     * 根据流程key获取最新版本的流程定义
     *
     * @param processDefinitionKey
     * @return
     */
    public ProcessDefinition getProcessDefinitionByKey(String processDefinitionKey) {
        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey);
        return query.latestVersion().singleResult();
    }

    /**
     * 根据流程定义获取最新版本的流程定义
     *
     * @param processDefinitionId
     * @return
     */
    public ProcessDefinition getProcessDefinitionById(String processDefinitionId) {
        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId);
        return query.latestVersion().singleResult();
    }

    /**
     * 获取某流程定义中的所有节点
     *
     * @param processDefinitionId 流程定义编号
     * @return
     */
    @Transactional(readOnly = true)
    public List<ActivityImpl> getAllActivities(String processDefinitionId) {
        ProcessDefinitionEntity processDefinitionEntity = getProcessDefinitionEntity(processDefinitionId);
        List<ActivityImpl> activities = processDefinitionEntity.getActivities();
        return activities;
    }

    /**
     * 根据流程定义获取流程中所有的任务节点
     *
     * @param processDefinitionId
     * @return
     */
    public List<ActivityImpl> getAllTaskActivity(String processDefinitionId) {

        List<ActivityImpl> userTasks = new ArrayList<ActivityImpl>();

        ProcessDefinitionEntity processDefinitionEntity = getProcessDefinitionEntity(processDefinitionId);
        List<ActivityImpl> activities = processDefinitionEntity.getActivities();
        for(ActivityImpl activity : activities){
            if("userTask".equals(activity.getProperty("type"))){
                userTasks.add(activity);
            }
        }
        return userTasks;
    }

    /**
     * 获取所有任务节点
     *
     * @param processDefinitionId
     * @return
     */
    public List<Map<String, Object>> getAllUserTaskActivity(String processDefinitionId) {
        List<ActivityImpl> activityList = getAllTaskActivity(processDefinitionId);
        List<Map<String,Object>> res = new ArrayList<Map<String, Object>>();

        for(ActivityImpl activity : activityList){
            Map<String,Object> map = Collections.synchronizedMap(new HashMap<String, Object>());
            map.put("taskId",activity.getId());
            map.put("taskName",activity.getProperty("name"));
            ActivityBehavior activityBehavior = activity.getActivityBehavior();
            if(activityBehavior instanceof UserTaskActivityBehavior){
                UserTaskActivityBehavior userTaskActivityBehavior = (UserTaskActivityBehavior)activityBehavior;
                if(userTaskActivityBehavior.getTaskDefinition().getAssigneeExpression()!=null){
                    map.put("taskAssignee",userTaskActivityBehavior.getTaskDefinition().getAssigneeExpression().getExpressionText());
                }
                else{
                    map.put("taskAssignee","");
                }
            }
            res.add(map);
        }

        return res;
    }

    public String getUserTaskAssignee(ActivityImpl activity){
        ActivityBehavior activityBehavior = activity.getActivityBehavior();
        if(activityBehavior instanceof UserTaskActivityBehavior){
            UserTaskActivityBehavior userTaskActivityBehavior = (UserTaskActivityBehavior)activityBehavior;
            if(userTaskActivityBehavior.getTaskDefinition().getAssigneeExpression()!=null){
                return  userTaskActivityBehavior.getTaskDefinition().getAssigneeExpression().getExpressionText();
            }
            else{
                return null;
            }
        }
        return null;
    }

    /**
     * 获取当前节点的直接后继节点
     *
     * @param processDefinitionId 流程定义编号
     * @param activityImpl        当前节点
     * @param activityId          当前节点编号
     * @param containsEndNode     是否要包含结束节点
     * @return 直接后继节点集合
     */
    public List<ActivityImpl> getNextNodes(String processDefinitionId, ActivityImpl activityImpl, String activityId,
                                           boolean containsEndNode) {
        List<ActivityImpl> activities = new ArrayList<ActivityImpl>();
        if(!activityId.equals(activityImpl.getId())){
            if(containsEndNode){
                if("userTask".equals(activityImpl.getProperty("type"))||
                        /*"serviceTask".equals(activityImpl.getProperty("type"))||*/
                        "endEvent".equals(activityImpl.getProperty("type"))){
                    activities.add(activityImpl);
                    return activities;
                }
            }
            else{
                if("userTask".equals(activityImpl.getProperty("type"))
                        /*|| "serviceTask".equals(activityImpl.getProperty("type"))*/){
                    activities.add(activityImpl);
                    return activities;
                }
            }
        }
        else{
            ProcessDefinitionEntity processDefinitionEntity = getProcessDefinitionEntity(processDefinitionId);
            ActivityImpl currentActivity = processDefinitionEntity.findActivity(activityId);

            //获取此结点的所有输出
            List<PvmTransition> outTransitions = currentActivity.getOutgoingTransitions();
            List<PvmTransition> outTransitionsTemp = null;
            for(PvmTransition tr:outTransitions){
                PvmActivity activity = tr.getDestination();
                if("exclusiveGateway".equals(activity.getProperty("type"))){
                    outTransitionsTemp = activity.getOutgoingTransitions();
                    for(PvmTransition tr1 : outTransitionsTemp){
                        activities.addAll(getNextNodes(processDefinitionId,(ActivityImpl) tr1.getDestination(),activityId,containsEndNode));
                    }
                }
                else{
                    activities.add(processDefinitionEntity.findActivity(activity.getId()));
                }
            }
        }

        return activities;
    }

    /**
     * 获取当前节点的直接后继节点
     *
     * @param processDefinitionId 流程定义编号
     * @param activityImpl        当前节点
     * @param activityId          当前节点编号
     * @param containsEndNode     是否要包含结束节点
     * @param excludeNodes        排除的节点
     * @return 直接后继节点集合
     */
    @Override
    public List<ActivityImpl> getNextNodes(String processDefinitionId, ActivityImpl activityImpl, String activityId, boolean containsEndNode, List<String> excludeNodes) {
        List<ActivityImpl> nextNodes = getNextNodes(processDefinitionId,activityImpl,activityId,containsEndNode);
        List<ActivityImpl> res = new ArrayList<>();
        for(ActivityImpl activity:nextNodes){
            if(!excludeNodes.contains(activity.getId())){
                res.add(activity);
            }
        }
        return res;
    }

    @Override
    public List<ActivityImpl> getNextAllNodes(String processDefinitionId, ActivityImpl activityImpl, String activityId, boolean containsEndNode) {
        List<ActivityImpl> activities = new ArrayList<ActivityImpl>();

        List<ActivityImpl> nextNodes =getNextNodes(processDefinitionId, activityImpl,activityId,containsEndNode);
        activities.addAll(nextNodes);
        for(ActivityImpl activity : nextNodes){
            if(activity.getProperty("type").equals("endEvent")){
                break;
            }
            activities.addAll(getNextAllNodes(processDefinitionId,activity,activity.getId(),containsEndNode));
        }
        return activities;
    }

    @Override
    public List<ActivityImpl> getNextAllNodes(String processDefinitionId, ActivityImpl activityImpl, String activityId, boolean containsEndNode, List<String> excludeNodes) {
        List<ActivityImpl> activities = new ArrayList<ActivityImpl>();

        List<ActivityImpl> nextNodes =getNextNodes(processDefinitionId, activityImpl,activityId,containsEndNode,excludeNodes);
        activities.addAll(nextNodes);
        for(ActivityImpl activity : nextNodes){
            if(activity.getProperty("type").equals("endEvent")){
                break;
            }
            activities.addAll(getNextAllNodes(processDefinitionId,activity,activity.getId(),containsEndNode,excludeNodes));
        }
        return activities;
    }

    /**
     * 获取当前节点的直接后继节点
     *
     * @param processDefinitionId 流程定义编号
     * @param activityImpl        当前节点
     * @param activityId          当前节点编号
     * @param elString
     * @param containsEndNode     是否要包含结束节点  @return 直接后继节点集合
     */
    public List<ActivityImpl> getNextNodes(String processDefinitionId, ActivityImpl activityImpl, String activityId, String elString, boolean containsEndNode) {
        List<ActivityImpl> activities = new ArrayList<ActivityImpl>();
        if(!activityId.equals(activityImpl.getId())){
            if(containsEndNode){
                if("userTask".equals(activityImpl.getProperty("type"))||
                        "serviceTask".equals(activityImpl.getProperty("type"))||
                        "endEvent".equals(activityImpl.getProperty("type"))){
                    activities.add(activityImpl);
                    return activities;
                }
            }
            else{
                if("userTask".equals(activityImpl.getProperty("type"))||
                        "serviceTask".equals(activityImpl.getProperty("type"))){
                    activities.add(activityImpl);
                    return activities;
                }
            }
        }
        else{
            ProcessDefinitionEntity processDefinitionEntity = getProcessDefinitionEntity(processDefinitionId);
            ActivityImpl currentActivity = processDefinitionEntity.findActivity(activityId);

            //获取此结点的所有输出
            List<PvmTransition> outTransitions = currentActivity.getOutgoingTransitions();
            List<PvmTransition> outTransitionsTemp = null;
            for(PvmTransition tr:outTransitions){
                PvmActivity activity = tr.getDestination();
                if("exclusiveGateway".equals(activity.getProperty("type"))){
                    outTransitionsTemp = activity.getOutgoingTransitions();
                    for(PvmTransition tr1 : outTransitionsTemp){
                    	if(StringUtils.isNotEmpty(elString)){
                            Object s = tr1.getProperty("conditionText");
                            if(StringUtils.trim(s.toString()).startsWith(elString)){
                                activities.addAll(getNextNodes(processDefinitionId,
                                        (ActivityImpl)tr1.getDestination(), activityId,elString,containsEndNode));
                            }
                    	}
                    	else{
                    		activities.addAll(getNextNodes(processDefinitionId,
                                    (ActivityImpl)tr1.getDestination(), activityId,elString,containsEndNode));
                    	}
                    }
                }
                else{
                    activities.add(processDefinitionEntity.findActivity(activity.getId()));
                }
            }
        }

        return activities;
    }
    
    @Override
	public List<ActivityImpl> getNextNodes(String processDefinitionId,
			ActivityImpl activityImpl, String activityId, String elString,
			boolean containsEndNode, List<String> excludeNodes) {
    	List<ActivityImpl> nextNodes = getNextNodes(processDefinitionId,activityImpl,activityId,elString,containsEndNode);
        List<ActivityImpl> res = new ArrayList<>();
        if(excludeNodes!=null) {
            for (ActivityImpl activity : nextNodes) {
                if (!excludeNodes.contains(activity.getId())) {
                    res.add(activity);
                }
            }
            return res;
        }
        return nextNodes;
	}

    /**
     * 根据节点编号获取ActivityImpl
     *
     * @param processDefinitionId 流程定义编号
     * @param currentActivityId   当前节点编号
     * @return ActivityImpl
     */
    public ActivityImpl findActivity(String processDefinitionId, String currentActivityId) {
        ProcessDefinitionEntity processDefinitionEntity = getProcessDefinitionEntity(processDefinitionId);
        ActivityImpl currentActivity = processDefinitionEntity.findActivity(currentActivityId);

        return currentActivity;
    }

    /**
     * 获取当前结点的直接前驱结点
     *
     * @param processDefinitionId 流程定义编号
     * @param currentActivityId   当前节点编号
     * @return
     */
    public List<ActivityImpl> getPresNodes(String processDefinitionId, String currentActivityId) {
        List<ActivityImpl> activities = new ArrayList<ActivityImpl>();

        ProcessDefinitionEntity processDefinitionEntity = getProcessDefinitionEntity(processDefinitionId);
        ActivityImpl currentActivity = processDefinitionEntity.findActivity(currentActivityId);
        //获取此结点的所有输入
        List<PvmTransition> inTransitions = currentActivity.getIncomingTransitions();
        for(PvmTransition tr:inTransitions){
            PvmActivity activity = tr.getSource();
            activities.add(processDefinitionEntity.findActivity(activity.getId()));
        }
        return activities;
    }

    /**
     * 递归获取当前结点的前驱结点
     *
     * @param processDefinitionId 流程定义编号
     * @param currentActivityId   当前节点编号
     * @return
     */
    @Override
    public List<ActivityImpl> getAllPreNodes(String processDefinitionId, String currentActivityId) {
        List<ActivityImpl> activities = new ArrayList<ActivityImpl>();

        List<ActivityImpl> preNodes =getPresNodes(processDefinitionId, currentActivityId);
        activities.addAll(preNodes);
        for(ActivityImpl activity : preNodes){
            if(activity.getProperty("type").equals("startEvent")){
                break;
            }
            activities.addAll(getAllPreNodes(processDefinitionId,activity.getId()));
        }
        return activities;
    }

    /**
     * 获取一个任务节点
     *
     * @param activityImpl 当前任务节点
     * @param activityId   当前任务编号
     * @param elString     el表达式
     * @return
     */
    public TaskDefinition getNextTaskDefinition(ActivityImpl activityImpl, String activityId, String elString) {
        if("userTask".equals(activityImpl.getProperty("type")) && !activityId.equals(activityImpl.getId())){
            TaskDefinition taskDefinition = ((UserTaskActivityBehavior)activityImpl.getActivityBehavior()).getTaskDefinition();
            return taskDefinition;
        }
        else{
            List<PvmTransition> outTransitions = activityImpl.getOutgoingTransitions();
            List<PvmTransition> outTransitionsTemp = null;
            for(PvmTransition tr:outTransitions){
                PvmActivity ac = tr.getDestination(); //获取线路的终点节点
                if("exclusiveGateway".equals(ac.getProperty("type"))){
                    outTransitionsTemp = ac.getOutgoingTransitions();
                    if(outTransitionsTemp.size() == 1){
                        return getNextTaskDefinition((ActivityImpl) outTransitionsTemp.get(0).getDestination(), activityId, elString);
                    }
                    else if(outTransitionsTemp.size() > 1){
                        for(PvmTransition tr1 : outTransitionsTemp){
                            Object s = tr1.getProperty("conditionText");
                            if(elString.equals(StringUtils.trim(s.toString()))){
                                return getNextTaskDefinition((ActivityImpl) tr1.getDestination(), activityId, elString);
                            }
                        }
                    }
                }
                else if("userTask".equals(ac.getProperty("type"))){
                    return ((UserTaskActivityBehavior)((ActivityImpl)ac).getActivityBehavior()).getTaskDefinition();
                }
            }
        }
        return null;
    }

    /**
     * 获取某个结点的下一个任务结点的集合
     *
     * @param activityImpl
     * @param activityId
     * @return
     */
    public List<TaskDefinition> getNextTaskDefinitions(ActivityImpl activityImpl, String activityId) {
        List<TaskDefinition> definitions = new ArrayList<TaskDefinition>();
        if("userTask".equals(activityImpl.getProperty("type")) && !activityId.equals(activityImpl.getId())){
            TaskDefinition taskDefinition = ((UserTaskActivityBehavior)activityImpl.getActivityBehavior()).getTaskDefinition();
            definitions.add(taskDefinition);
            return definitions;
        }
        else{
            List<PvmTransition> outTransitions = activityImpl.getOutgoingTransitions();
            List<PvmTransition> outTransitionsTemp = null;
            for(PvmTransition tr:outTransitions){
                PvmActivity ac = tr.getDestination(); //获取线路的终点节点
                if("exclusiveGateway".equals(ac.getProperty("type"))){
                    outTransitionsTemp = ac.getOutgoingTransitions();
                    if(outTransitionsTemp.size() == 1){
                        definitions.addAll(getNextTaskDefinitions((ActivityImpl) outTransitionsTemp.get(0).getDestination(), activityId));
                    }
                    else if(outTransitionsTemp.size() > 1){
                        for(PvmTransition tr1 : outTransitionsTemp){
                            definitions.addAll(getNextTaskDefinitions((ActivityImpl) tr1.getDestination(), activityId));
                        }
                    }
                }
                else if("userTask".equals(ac.getProperty("type"))){
                    definitions.add(((UserTaskActivityBehavior)((ActivityImpl)ac).getActivityBehavior()).getTaskDefinition());
                }
            }
        }
        return definitions;
    }

    /**
     * 流程定义图预览
     *
     * @param processDefinitionId
     * @return
     */
    public byte[] viewProcessDefinitionDiagram(String processDefinitionId) throws Exception{
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);

        ProcessEngineImpl defaultProcessEngine = (ProcessEngineImpl) ProcessEngines
                .getDefaultProcessEngine();

        Context.setProcessEngineConfiguration(defaultProcessEngine
                .getProcessEngineConfiguration());

        ProcessEngineConfiguration processEngineConfiguration = processEngine
                .getProcessEngineConfiguration();

        ProcessDiagramGenerator diagramGenerator = processEngineConfiguration
                .getProcessDiagramGenerator();

        InputStream imageStream = diagramGenerator.generateDiagram(bpmnModel,
                "png", processEngineConfiguration.getActivityFontName(),
                processEngineConfiguration.getLabelFontName(),
                processEngineConfiguration.getClassLoader(), 1.0);

        return IOUtils.toByteArray(imageStream);
    }

    public byte[] viewProcessDefinitionDiagram(String resourceType,String processDefinitionId) throws Exception{
        InputStream inputStream = null;

        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId)
                .singleResult();

        String resourceName = "";

        if(resourceType.toLowerCase().equals("image")){
            resourceName = processDefinition.getDiagramResourceName();
        }
        else if(resourceType.toLowerCase().equals("xml")){
            resourceName = processDefinition.getResourceName();
        }

        inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),resourceName);

        return IOUtils.toByteArray(inputStream);
    }

    /**
     * 获取所有的流程定义实例
     *
     * @param start
     * @param limit
     * @param sort
     * @param dir
     * @return
     */
    public Map<String,Object> findAllProcessDefinitionMap(Integer start, Integer limit, String sort, String dir) {

        Map<String,Object> map = new HashMap<String, Object>();

        List<ProcessDefinition> list = findAllProcessDefinition(start,limit,sort, dir);

        List<Map<String, Object>> processDefinitions = new ArrayList<Map<String, Object>>();
        for(ProcessDefinition processDefinition : list) {
            Map<String, Object> temp = new HashMap<String, Object>();
            temp.put("id", processDefinition.getId());
            temp.put("key", processDefinition.getKey());
            temp.put("name", processDefinition.getName());
            temp.put("deploymentId", processDefinition.getDeploymentId());
            temp.put("status", processDefinition.isSuspended());
            temp.put("version", processDefinition.getVersion());
            temp.put("resourceName", processDefinition.getDiagramResourceName());
            temp.put("description", processDefinition.getDescription());
            processDefinitions.add(temp);
        }

        long records = countAllProcessDefinition();
        long total = 0;

        if(records%limit==0){
            total = records/limit;
        }
        else{
            total = records/limit + 1;
        }

        map.put(Constants.DEFAULT_RECORD_MODEL_KEY, processDefinitions);
        map.put(Constants.DEFAULT_COUNT_MODEL_KEY, total);//总页数
        map.put(Constants.DEFAULT_RECORDS_MODEL_KEY,records);//总记录数目
        map.put(Constants.DEFAULT_SUCCESS_KEY, Boolean.TRUE);

        return map;
    }

    private List<ProcessDefinition> findAllProcessDefinition(String sort, String dir){
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery()
                .orderByProcessDefinitionKey().latestVersion();
        if(dir.toLowerCase().equals(Constants.DEFAULT_SORT_DIRECTION)) {
            processDefinitionQuery = processDefinitionQuery.asc();
        } else {
            processDefinitionQuery = processDefinitionQuery.desc();
        }
        return processDefinitionQuery.list();
    }

    public List<ProcessDefinition> findAllProcessDefinition(Integer start, Integer limit,String sort, String dir){
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery()
                .orderByProcessDefinitionKey().latestVersion();
        if(dir.toLowerCase().equals(Constants.DEFAULT_SORT_DIRECTION)) {
            processDefinitionQuery = processDefinitionQuery.asc();
        } else {
            processDefinitionQuery = processDefinitionQuery.desc();
        }
        if(start != null && limit != null) {
            return processDefinitionQuery.listPage(start, limit);
        }
        return processDefinitionQuery.list();
    }

    /**
     * 计算所有的流程定义实例
     *
     * @return
     */
    public long countAllProcessDefinition() {
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery().latestVersion();
        return processDefinitionQuery.count();
    }

    /**
     * 导入流程定义
     *
     * @param fileName
     * @param bs
     */
    public void importProcessDefinition(String fileName, byte[] bs) {

        InputStream inputStream = new ByteArrayInputStream(bs);

        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();

        if (fileName.endsWith(".bpmn20.xml") || fileName.endsWith(".bpmn")) {
            deploymentBuilder.addInputStream(fileName, inputStream);
        }
        else if (fileName.endsWith(".bar") || fileName.endsWith(".zip")) {
            deploymentBuilder.addZipInputStream(new ZipInputStream(inputStream));
        }

        deploymentBuilder.deploy();
    }

    /**
     * 删除流程定义
     *
     * @param definition
     */
    public void deleteProcessDefinition(ProcessDefinition definition) {
        deleteProcessDefinition(definition.getDeploymentId());
    }

    /**
     * 根据部署id删除流程定义
     *
     * @param deploymentId
     */
    public void deleteProcessDefinition(String deploymentId) {
        repositoryService.deleteDeployment(deploymentId,true);
    }
}
